package com.wwu.system.common.aspect;

import com.alibaba.fastjson2.JSON;
import com.wwu.common.utils.SpringUtils;
import com.wwu.system.common.security.SecurityUtil;
import com.wwu.system.entity.OperLog;
import com.wwu.system.service.IOperLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * 操作日志记录切面
 * @author 一蓑烟雨
 * @version 1.0.0
 * @date 2023-07-27 15:02
 */
@Slf4j
@Aspect
@Component
public class LogAspect {
    @Resource
    private IOperLogService logService;

    private ThreadLocal<LocalDateTime> startTime = new ThreadLocal<LocalDateTime>();

    /**
     * 请求前记录时间
     * @param joinPoint 切入点
     * @author 一蓑烟雨
     * @date 2023/7/28 10:34
     */
    @Before("@annotation(controllerLog)")
    public void doBefore(JoinPoint joinPoint, Log controllerLog){
        startTime.set(LocalDateTime.now());
    }

    /**
     * 请求处理成功记录日志
     * @param joinPoint 切入点
     * @param controllerLog
     * @param jsonResult 正常结果
     * @author 一蓑烟雨
     * @date 2023/7/27 19:23
     */
    @AfterReturning(pointcut = "@annotation(controllerLog)",returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult){
        handleLog(joinPoint,controllerLog,null,jsonResult);
    }

    /**
     * 请求处理异常记录日志
     * @param joinPoint 切入点
     * @param controllerLog
     * @param e 异常信息
     * @author 一蓑烟雨
     * @date 2023/7/27 19:27
     */
    @AfterThrowing(pointcut = "@annotation(controllerLog)",throwing = "e")
    public void AfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e){
        handleLog(joinPoint,controllerLog,e,null);
    }

    /**
     * 异步记录操作日志
     * @param joinPoint
     * @param controllerLog
     * @param e
     * @param jsonResult
     * @author 一蓑烟雨
     * @date 2023/7/28 10:17
     */
    public void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult){
        if(!getOperLogAnnotation(joinPoint)){
          return;
        }

        OperLog operLog = new OperLog();
        HttpServletRequest request = SpringUtils.getHttpServletRequest();
        operLog.setSessionId(SpringUtils.getHttpSession(request).getId());
        operLog.setOperModule(controllerLog.menuName());
        operLog.setOperType(controllerLog.menuBtnEnum().getName());
        operLog.setRequestMethod(SpringUtils.getRequestMethod(request));
        operLog.setRequestUrl(SpringUtils.getRequestURI(request));

        //拼接请求的类及方法名
        String className = joinPoint.getTarget().getClass().getName();
        String classMethod = joinPoint.getSignature().getName();
        operLog.setRequestClass(className+"."+classMethod);
        //判断是否保存请求参数
        if(controllerLog.saveRequestData()){
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            String[] paramNames = signature.getParameterNames();
            Object[] paramValue = joinPoint.getArgs();
            if (paramNames != null && paramNames.length > 0) {
                Map<String, Object> map = new HashMap<>();
                for (int i = 0; i < paramNames.length; i++) {
                    map.put(paramNames[i], paramValue[i]);
                }
                String paramStr = StringUtils.hasText(map.toString()) && map.toString().length() < 5000 ? map.toString() : map.toString().substring(0,4999);
                operLog.setRequestParam(paramStr);
            }
        }
        //判断是否保存响应参数
        if(controllerLog.saveResponseData() && !ObjectUtils.isEmpty(jsonResult)){
            String jsonStr = JSON.toJSONString(jsonResult);
            jsonStr = StringUtils.hasText(jsonStr) && jsonStr.length() < 5000 ? jsonStr : jsonStr.substring(0,4999);
            operLog.setResponseResult(jsonStr);
        }
        operLog.setUserName(SecurityUtil.getCurrUserName());
        HashMap<String, String> hashMap = SpringUtils.getBrowserAndOsInfo(request);
        operLog.setBrowser(hashMap.get("browserName"));
        operLog.setOs(hashMap.get("osName"));
        operLog.setOperIp(SpringUtils.getIpAddress(request));
        operLog.setOperRemoteHost(SpringUtils.getRemoteHost(request));
        operLog.setOperStatus(ObjectUtils.isEmpty(e) ? 0:1);
        operLog.setOperDesc(ObjectUtils.isEmpty(e) ? "操作成功":e.getMessage());
        operLog.setOperStartTime(startTime.get());
        operLog.setOperEndTime(LocalDateTime.now());
        operLog.setOperExecuteDuration(Duration.between(startTime.get(),LocalDateTime.now()).getSeconds());

        //异步记录操作日志
        logService.saveOperLog(operLog);
    }

    /**
     * 存在日志注解时记录日志
     * @param joinPoint
     * @return boolean
     * @author 一蓑烟雨
     * @date 2023/7/28 10:16
     */
    private boolean getOperLogAnnotation(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(Log.class) != null ? true: false;
        }
        return false;
    }
}
